home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.1 (Developer) [x86] / NeXT Step 3.1 Intel dev.cdr.dmg / NextDeveloper / Examples / AppKit / CompositeLab / CompositeView.m < prev    next >
Text File  |  1993-01-05  |  14KB  |  493 lines

  1. /*
  2.  
  3.  You may freely copy, distribute and reuse the code in this example.
  4.  NeXT disclaims any warranty of any kind, expressed or implied,
  5.  as to its fitness for any particular use.
  6.  
  7.  CompositeView implements a view with three horizontal, equal-sized areas.
  8.  The left-most area is the "source," the middle area is the "destination,"
  9.  and the right-most area is the "result." CompositeView assures that the
  10.  contents of the result area is always generated by compositing the other
  11.  two areas using the compositing mode set in the setOperator: method.
  12.  It is also possible to change the contents, color, and alpha of the
  13.  source and destination areas; see the methods setSourceColor:, 
  14.  setSourceAlpha:, etc.
  15.  
  16.  CompositeView also demonstrates some of the drag & drop features of
  17.  NeXTSTEP 3.0 by acting as a destination for colors & images.
  18.  
  19.  CompositeView written by Bruce Blumberg and Ali Ozer.
  20.  
  21.  Color support, NXColorPanel, NXColorWells, and NXImage added during 1990 by Ali.
  22.  Color dragging support added Feb 9, 1992, by Ali.
  23.  Image dragging support added May 27, 1992, by Ali.
  24.  Image dragging problem fixed Sep 29, 1992 (for 3.1), by Ali. See QAP appkit.878.
  25.  
  26. */
  27.  
  28. #import <appkit/appkit.h>
  29. #import "CompositeView.h"
  30.  
  31.  
  32. @implementation CompositeView
  33.  
  34. // The possible draw modes for the source.
  35.  
  36. #define TRIANGLE 0
  37. #define CIRCLE   1
  38. #define DIAMOND  2
  39. #define HEART    3
  40. #define FLOWER   4
  41. #define CUSTOM   5
  42.  
  43. // initFrame: creates the view, initializes the rectangles that define the
  44. // three areas described above, and creates the bitmaps used for rendering the
  45. // source and destination bitmaps. newFrame: is a convenience method.
  46.  
  47. - initFrame:(const NXRect *)tF
  48. {
  49.     // Initialize the view
  50.     [super initFrame:tF];
  51.  
  52.     // Make rectangles for source, destination and result
  53.     sRect = bounds;
  54.     sRect.size.width /= 3.0;
  55.     dRect = sRect;
  56.     dRect.origin.x = sRect.size.width;
  57.     rRect = dRect;
  58.     rRect.origin.x = dRect.origin.x + dRect.size.width;
  59.  
  60.     // Create source, destination, and result images.
  61.  
  62.     [(source = [[NXImage allocFromZone:[self zone]] initSize:&sRect.size])
  63.     useDrawMethod:@selector(drawSource:) inObject:self];
  64.     [source setBackgroundColor:NX_COLORCLEAR];
  65.     
  66.     [(destination = [[NXImage allocFromZone:[self zone]] initSize:&dRect.size])
  67.     useDrawMethod:@selector(drawDestination:) inObject:self];
  68.     [destination setBackgroundColor:NX_COLORCLEAR];
  69.  
  70.     [(result = [[NXImage allocFromZone:[self zone]] initSize:&dRect.size])
  71.     useDrawMethod:@selector(drawResult:) inObject:self];
  72.     [result setBackgroundColor:NX_COLORCLEAR];
  73.  
  74.     // Set the default operator and source picture. No need to set the default
  75.     // colors; these are read from the .nib file when the outlets to the wells
  76.     // are estanblished.
  77.  
  78.     operator = NX_COPY;
  79.     sourcePicture = TRIANGLE;
  80.  
  81.     // Tell the application that alpha should be allowed in the color panel
  82.     // and dragged colors. Most apps do not want to bother with this.
  83.  
  84.     [NXApp setImportAlpha:YES];
  85.  
  86.     // Finally, register for dragging colors and files. 
  87.  
  88.     [self registerForDraggedTypes:&NXColorPboardType count:1];
  89.     [self registerForDraggedTypes:&NXFilenamePboardType count:1];
  90.  
  91.     return self;
  92. }
  93.  
  94. // Get handles to the wells and read their initial colors.
  95.  
  96. - setSourceColorWell:anObject
  97. {
  98.     sourceColorWell = anObject;
  99.     sourceColor = [anObject color];
  100.     return self;
  101. }
  102.  
  103. - setDestColorWell:anObject
  104. {
  105.     destColorWell = anObject;
  106.     destColor = [anObject color];
  107.     return self;
  108. }
  109.  
  110. - setBackColorWell:anObject
  111. {
  112.     backColorWell = anObject;
  113.     backgroundColor = [anObject color];
  114.     return self;
  115. }
  116.  
  117. // drawSource creates the source image in the source bitmap. Note that
  118. // drawSource does not render in the view; it renders in the bitmap only.
  119.  
  120. - drawSource:image
  121. {    
  122.     NXPoint zeroPoint = {0.0, 0.0};
  123.  
  124.     NXSetColor(sourceColor);
  125.     PSnewpath();
  126.     switch (sourcePicture) {
  127.  
  128.     case TRIANGLE: 
  129.          PSmoveto (0.0, 0.0);
  130.         PSlineto (0.0, sRect.size.height);
  131.         PSlineto (sRect.size.width, sRect.size.height);
  132.         break;
  133.  
  134.     case CIRCLE:
  135.         PSscale (sRect.size.width, sRect.size.height);
  136.         PSarc (0.5, 0.5, 0.4, 0.0, 360.0);  // diameter is 80% of area
  137.          break;
  138.  
  139.     case DIAMOND:
  140.          PSmoveto (0.0, sRect.size.height / 2.0);
  141.         PSlineto (sRect.size.width / 2.0, 0.0);
  142.         PSlineto (sRect.size.width, sRect.size.height / 2.0);
  143.         PSlineto (sRect.size.width / 2.0, sRect.size.height);
  144.         break;
  145.  
  146.     case HEART:
  147.         PSscale (sRect.size.width, sRect.size.height);
  148.         PSmoveto (0.5, 0.5);
  149.         PScurveto (0.3, 1.0, 0.0, 0.5, 0.5, 0.1);
  150.         PSmoveto (0.5, 0.5);            
  151.         PScurveto (0.7, 1.0, 1.0, 0.5, 0.5, 0.1);  
  152.         break;
  153.  
  154.     case FLOWER:
  155.         PSscale (sRect.size.width, sRect.size.height);
  156.         PStranslate (0.5, 0.5);
  157.         PSmoveto (0.0, 0.0); 
  158.             {int cnt;
  159.          for (cnt = 0; cnt < 6; cnt++) {
  160.         PSrotate (60.0);
  161.         PScurveto (0.4, 0.5, -0.4, 0.5, 0.0, 0.0);
  162.          }
  163.         }
  164.         break;
  165.  
  166.      case CUSTOM:
  167.         if (!customImage) {
  168.         customImage = [[NXImage allocFromZone:[self zone]] 
  169.                 initSize:&rRect.size];
  170.         [customImage setScalable:YES];
  171.         [customImage useFromSection:"DefaultCustomImage.eps"];
  172.         }
  173.         [customImage composite:NX_SOVER toPoint:&zeroPoint];
  174.         break;
  175.  
  176.     default:
  177.         break;
  178.     }
  179.     PSclosepath();
  180.     PSfill();
  181.  
  182.     return self;
  183. }
  184.  
  185. // drawDestination creates the destination image in the destination bitmap. 
  186. // Like drawSource, drawDestination only draws in the bitmap, not the view.
  187.  
  188. - drawDestination:image
  189. {
  190.     NXSetColor(destColor);
  191.     PSnewpath();
  192.     PSmoveto(dRect.size.width, 0.0);
  193.     PSlineto(dRect.size.width, dRect.size.height);
  194.     PSlineto(0.0, dRect.size.height);
  195.     PSclosepath();
  196.     PSfill();
  197.     return self;
  198. }
  199.  
  200. // drawResults creates the resulting image, formed by compositing the
  201. // source image after the destination image with the specified operator. 
  202.  
  203. - drawResult:image
  204. {
  205.     NXPoint zeroPoint = {0.0, 0.0};
  206.  
  207.     [destination composite:NX_COPY toPoint:&zeroPoint];
  208.     [source composite:operator toPoint:&zeroPoint];
  209.     return self;
  210. }
  211.     
  212. // setSourcePicture allows setting the picture to be drawn in the source
  213. // bitmap. Buttons connected to this method should have tags that are 
  214. // set to the various possible pictures (see the "#define"s, above).
  215. //
  216. // After setting the sourcePicture instance variable, setSourcePicture redraws
  217. // the bitmap and updates the view to reflect the new configuration.
  218.  
  219. - setSourcePicture:sender
  220. {
  221.     sourcePicture = [sender selectedTag];
  222.     [source recache];
  223.     [result recache];
  224.     [self display];
  225.     return self;
  226. }
  227.  
  228. - (BOOL)changeCustomImageTo:newImage
  229. {
  230.     if (newImage) {
  231.     [newImage setSize:&rRect.size];
  232.     [newImage setScalable:YES];
  233.     if ([newImage lockFocus]) {    // Is this a good image indeed?
  234.         [newImage unlockFocus];
  235.         [customImage free];
  236.         customImage = newImage;
  237.         if (sourcePicture != CUSTOM) {
  238.         sourcePicture = CUSTOM;
  239.         [sourcePictureMatrix selectCellWithTag:CUSTOM];
  240.         }
  241.         [source recache];
  242.         [result recache];
  243.         [self display];
  244.         return YES;
  245.     }
  246.     }
  247.     return NO;
  248. }
  249.  
  250. - changeCustomImage:sender
  251. {
  252.     if ([[OpenPanel new] runModalForTypes:[NXImage imageFileTypes]]) {
  253.     const char *fileName = [[OpenPanel new] filename];
  254.     (void)[self changeCustomImageTo:[[NXImage allocFromZone:[self zone]] initFromFile:fileName]];
  255.     }
  256.  
  257.     return self;
  258. }
  259.  
  260. // The following methods change the colors and update
  261. // the source or destination bitmaps and the view to reflect the change.
  262. // They should typically be called by a control capable of returning
  263. // a color (for instance, an NXColorWell).
  264.  
  265. - changeSourceColor:sender
  266. {
  267.     [self changeSourceColorTo:[sender color] andDisplay:YES];
  268.     return self;
  269. }
  270.  
  271. - changeDestColor:sender
  272. {
  273.     [self changeDestColorTo:[sender color] andDisplay:YES];
  274.     return self;
  275. }
  276.  
  277. - changeBackgroundColor:sender
  278. {
  279.     [self changeBackgroundColorTo:[sender color] andDisplay:YES];
  280.     return self;
  281. }
  282.  
  283. - (void)changeSourceColorTo:(NXColor)color andDisplay:(BOOL)flag
  284. {
  285.     if (!NXEqualColor(sourceColor, color)) {
  286.     sourceColor = color;
  287.     [source recache];
  288.     [result recache];
  289.     if (flag) [self display];
  290.     }
  291. }
  292.  
  293. - (void)changeDestColorTo:(NXColor)color andDisplay:(BOOL)flag
  294. {
  295.     if (!NXEqualColor(destColor, color)) {
  296.     destColor = color;
  297.     [destination recache];
  298.     [result recache];
  299.     if (flag) [self display];
  300.     }
  301. }
  302.  
  303. - (void)changeBackgroundColorTo:(NXColor)color andDisplay:(BOOL)flag
  304. {
  305.     if (!NXEqualColor(backgroundColor, color)) {
  306.     backgroundColor = color;
  307.     if (flag) [self display];
  308.     }
  309. }
  310.  
  311. // The operator method returns the operator currently in use.
  312.  
  313. - (int)operator {return operator;}
  314.  
  315.  
  316. // setOperator sets the operator to be used in the compositing operations
  317. // and updates the view to reflect the change. Note that setOperator needs
  318. // to be connected to a row of buttons.
  319.  
  320. - setOperator:sender
  321. {    
  322.     switch ([sender selectedRow]) {
  323.     case 0: operator = NX_COPY;        break;
  324.     case 1: operator = NX_CLEAR;         break;
  325.     case 2: operator = NX_SOVER;         break;
  326.     case 3: operator = NX_DOVER;        break;
  327.     case 4: operator = NX_SIN;         break;
  328.     case 5: operator = NX_DIN;         break;
  329.     case 6: operator = NX_SOUT;        break;
  330.     case 7: operator = NX_DOUT;        break;
  331.     case 8: operator = NX_SATOP;        break; 
  332.     case 9: operator = NX_DATOP;        break;
  333.     case 10: operator = NX_XOR;         break;
  334.     case 11: operator = NX_PLUSD;        break;
  335.     case 12: operator = NX_PLUSL;        break;
  336.     default: break;
  337.     }
  338.     [result recache];
  339.     [self speedyDraw];
  340.  
  341.     return self;
  342. }
  343.  
  344.         
  345. // drawSelf:: simply redisplays the contents of the view. The source and
  346. // destination rectangles are updated from the bitmaps while the result
  347. // rectangle is created by compositing the two bitmaps.
  348.  
  349. - drawSelf:(NXRect *)r :(int) count
  350. {
  351.     // Erase the whole view
  352.     NXSetColor (backgroundColor);
  353.     NXRectFill (&bounds);
  354.  
  355.     // Color for the frame of the three sections...
  356.     NXSetColor (NXChangeAlphaComponent (NX_COLORBLACK, 1.0));
  357.  
  358.     // Draw the source bitmap and then frame it with black
  359.     [source composite:NX_SOVER toPoint:&sRect.origin];
  360.     NXFrameRect(&sRect);
  361.  
  362.     // Draw the destination bitmap and frame it with black 
  363.     [destination composite:NX_SOVER toPoint:&dRect.origin];
  364.     NXFrameRect(&dRect);
  365.  
  366.     // And now for the result image. Frame it with black as well
  367.     [result composite:NX_SOVER toPoint:&rRect.origin];
  368.     NXFrameRect(&rRect);
  369.  
  370.     return self;
  371. }
  372.  
  373. // speedyDraw provides some efficiency in redisplaying the view by assuming
  374. // that the source and the destination rectangles are already in place.
  375.  
  376. - speedyDraw
  377. {
  378.     [self lockFocus];
  379.     NXSetColor (backgroundColor);
  380.     NXRectFill (&rRect);
  381.     [result composite:NX_SOVER toPoint:&rRect.origin];
  382.     NXSetColor (NXChangeAlphaComponent (NX_COLORBLACK, 1.0));
  383.     NXFrameRect(&rRect);
  384.     [[self window] flushWindow];
  385.     [self unlockFocus];
  386.  
  387.     return self;
  388. }
  389.  
  390. // free method to free all the images along with the view.
  391.  
  392. - free
  393. {
  394.     [source free];
  395.     [destination free];
  396.     [result free];
  397.     [customImage free];
  398.     return [super free];
  399. }
  400.  
  401.  
  402. // Code to support dragging...
  403. // This is mostly complicated by the fact that the code wants to demonstrate
  404. // how to dynamically give feedback to the user as the colors are
  405. // dragged (but not dropped). We don't dynamically give feedback when images
  406. // are dragged (as it might take a long time).
  407.  
  408. // includesType() returns YES if type is included within types.
  409.  
  410. static BOOL includesType (const NXAtom *types, NXAtom type)
  411. {
  412.     if (types) while (*types) if (*types++ == type) return YES;
  413.     return NO;
  414. }
  415.  
  416. - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
  417. {
  418.     return [self draggingUpdated:sender];
  419. }
  420.  
  421. - (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
  422. {
  423.     if ([sender draggingSourceOperationMask] & NX_DragOperationGeneric) {
  424.     Pasteboard *pboard = [sender draggingPasteboard];
  425.         if (includesType([pboard types], NXColorPboardType)) {    // Color
  426.         NXColor sourceColorSave = sourceColor;
  427.         NXColor destColorSave = destColor;
  428.         NXColor backgroundColorSave = backgroundColor;
  429.         [self doColorDrag:sender];
  430.         [self changeSourceColorTo:sourceColorSave andDisplay:NO];
  431.         [self changeDestColorTo:destColorSave andDisplay:NO];
  432.         [self changeBackgroundColorTo:backgroundColorSave andDisplay:NO];
  433.         return NX_DragOperationGeneric;
  434.     } else if ([NXImage canInitFromPasteboard:pboard]) {    // Image?
  435.         return NX_DragOperationGeneric;
  436.     }
  437.     }
  438.     return NX_DragOperationNone;        
  439. }
  440.  
  441. - draggingExited:sender
  442. {
  443.     if (includesType([[sender draggingPasteboard] types], NXColorPboardType)) {    // We need to fix the view up
  444.     [self display];
  445.     }
  446.     return self;
  447. }
  448.  
  449. - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender
  450. {
  451.     return ([self draggingUpdated:sender] == NX_DragOperationNone) ? NO : YES;
  452. }
  453.  
  454. - concludeDragOperation:(id <NXDraggingInfo>)sender
  455. {
  456.     Pasteboard *pboard = [sender draggingPasteboard];
  457.  
  458.     if (includesType([pboard types], NXColorPboardType)) {
  459.     [self doColorDrag:sender];
  460.     [sourceColorWell setColor:sourceColor];
  461.     [destColorWell setColor:destColor];
  462.     [backColorWell setColor:backgroundColor];
  463.     } else {
  464.         (void)[self changeCustomImageTo:[[NXImage allocFromZone:[self zone]] initFromPasteboard:pboard]];
  465.     }
  466.     return self;
  467. }
  468.  
  469. - (void)doColorDrag:(id <NXDraggingInfo>)sender
  470. {
  471.     NXPoint p = [sender draggingLocation];
  472.     NXColor c = NXReadColorFromPasteboard([sender draggingPasteboard]);
  473.  
  474.     [self convertPoint:&p fromView:nil];
  475.  
  476.     switch ((int)(3 * p.x / NX_WIDTH(&bounds))) {
  477.     case 0:
  478.         [self changeSourceColorTo:c andDisplay:YES];
  479.         break;
  480.     case 1:
  481.         [self changeDestColorTo:c andDisplay:YES];
  482.         break;
  483.     case 2:
  484.         [self changeBackgroundColorTo:c andDisplay:YES];
  485.         break;
  486.     default:
  487.         break;    // Shouldn't really happen...
  488.     }
  489. }
  490.  
  491.  
  492. @end
  493.